import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, filedialog
import threading
from openai import OpenAI
import json
import os
from datetime import datetime


class DeepSeekChatApp:
    def __init__(self, root):
        self.root = root
        self.root.title("DeepSeek AI对话助手")
        self.root.geometry("800x600")
        self.root.configure(bg="#f0f2f5")

        # 配置文件
        self.config_file = "deepseek_config.json"

        # 初始化配置
        self.api_key = "DeepSeek APIkey"  # 默认值
        self.user_display_name = "您"
        self.ai_display_name = "AI助手"
        self.current_model = "deepseek-chat"

        # 加载配置
        self.load_config()

        # 初始化DeepSeek客户端
        self.client = OpenAI(
            api_key=self.api_key,
            base_url="https://api.deepseek.com"
        )



        # 对话历史
        self.conversation_history = [
            {"role": "system", "content": self.system_prompt}
        ]

        # 创建界面
        self.setup_ui()

    def load_config(self):
        """从配置文件加载设置"""
        if os.path.exists(self.config_file):
            try:
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)

                self.api_key = config.get('api_key', "DeepSeek APIkey")
                self.user_display_name = config.get('user_display_name', '您')
                self.ai_display_name = config.get('ai_display_name', 'AI助手')
                self.system_prompt = config.get('system_prompt',
                                                '你是一个乐于助人的AI助手，请用简洁友好的方式回答用户问题。')

            except Exception as e:
                print(f"加载配置失败: {e}")

    def save_config(self):
        """保存配置到文件"""
        try:
            config = {
                'api_key': self.api_key,
                'user_display_name': self.user_display_name,
                'ai_display_name': self.ai_display_name,
                'system_prompt': self.system_prompt
            }

            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)

            print("配置已保存到文件")  # 调试信息

        except Exception as e:
            print(f"保存配置失败: {e}")

    def setup_ui(self):
        """设置用户界面"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题栏框架 - 使用 grid 布局
        title_frame = ttk.Frame(main_frame)
        title_frame.pack(fill=tk.X, pady=(0, 10))

        # 左侧标题标签
        title_label = ttk.Label(title_frame, text="DeepSeek AI对话助手",
                                font=("Arial", 16, "bold"), foreground="#2c3e50")
        title_label.grid(row=0, column=0, sticky="w")

        # 模型选择框架（右侧）
        model_frame = ttk.Frame(title_frame)
        model_frame.grid(row=0, column=1, sticky="e")

        ttk.Label(model_frame, text="模型:").pack(side=tk.LEFT, padx=(0, 5))
        self.model_var = tk.StringVar(value=self.current_model)
        model_combo = ttk.Combobox(model_frame, textvariable=self.model_var,
                                   values=["deepseek-chat", "deepseek-reasoner", "deepseek-coder"],
                                   width=15, state="readonly")
        model_combo.pack(side=tk.LEFT)
        model_combo.bind("<<ComboboxSelected>>", self.on_model_change)

        # 按钮容器框架 - 放置在标题栏最右侧
        button_container = ttk.Frame(title_frame)
        button_container.grid(row=0, column=2, sticky="e", padx=(10, 0))

        # 将按钮添加到容器中，水平排列
        save_btn = ttk.Button(button_container, text="保存", command=self.save_conversation, width=6)
        save_btn.pack(side=tk.LEFT, padx=2)

        load_btn = ttk.Button(button_container, text="加载", command=self.load_conversation, width=6)
        load_btn.pack(side=tk.LEFT, padx=2)

        settings_btn = ttk.Button(button_container, text="设置", command=self.show_settings_window, width=6)
        settings_btn.pack(side=tk.LEFT, padx=2)

        # 配置标题栏的列权重
        title_frame.columnconfigure(0, weight=1)
        title_frame.columnconfigure(1, weight=0)
        title_frame.columnconfigure(2, weight=0)

        # 对话显示区域
        chat_frame = ttk.LabelFrame(main_frame, text="对话记录", padding="5")
        chat_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        self.chat_display = scrolledtext.ScrolledText(chat_frame, wrap=tk.WORD,
                                                      font=("Arial", 11), bg="white",
                                                      padx=10, pady=10, state=tk.DISABLED)
        self.chat_display.pack(fill=tk.BOTH, expand=True)

        # 配置文本样式 - 添加思考过程样式
        self.chat_display.tag_config("user", foreground="#1e88e5", font=("Arial", 11, "bold"))
        self.chat_display.tag_config("assistant", foreground="#43a047", font=("Arial", 11, "bold"))
        self.chat_display.tag_config("system", foreground="#999999", font=("Arial", 10, "italic"))
        self.chat_display.tag_config("error", foreground="#e53935", font=("Arial", 11, "bold"))
        self.chat_display.tag_config("reasoning", foreground="#ff9800", font=("Arial", 10, "italic"))  # 思考过程样式

        # 输入区域
        input_frame = ttk.Frame(main_frame)
        input_frame.pack(fill=tk.X, pady=(5, 0))

        # 输入文本框
        self.input_text = scrolledtext.ScrolledText(input_frame, height=3, wrap=tk.WORD,
                                                    font=("Arial", 11), padx=10, pady=10)
        self.input_text.pack(fill=tk.X, expand=True, side=tk.LEFT, padx=(0, 5))
        self.input_text.bind("<Return>", self.on_enter_pressed)
        self.input_text.focus_set()

        # 按钮框架
        btn_frame = ttk.Frame(input_frame)
        btn_frame.pack(side=tk.RIGHT, fill=tk.Y)

        # 发送按钮
        self.send_btn = ttk.Button(btn_frame, text="发送", command=self.send_message, width=8)
        self.send_btn.pack(pady=(0, 5))

        # 清空按钮
        clear_btn = ttk.Button(btn_frame, text="清空", command=self.clear_conversation, width=8)
        clear_btn.pack(pady=5)

        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set(f"就绪 | 模型: {self.current_model} | 用户: {self.user_display_name}")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)

        # 显示欢迎消息
        self.add_message("system", "对话已开始！请输入您的问题。")

    def on_model_change(self, event):
        """处理模型切换事件"""
        new_model = self.model_var.get()
        if new_model != self.current_model:
            self.current_model = new_model
            self.add_message("system", f"已切换到模型: {new_model}")
            self.status_var.set(f"就绪 | 模型: {new_model} | 用户: {self.user_display_name}")

    def on_enter_pressed(self, event):
        """处理回车键按下事件"""
        if event.state == 0 or event.state == 1:  # 没有按下Shift键
            self.send_message()
            return "break"
        return None

    def add_message(self, role, content, add_timestamp=True):
        """添加消息到对话显示区域"""
        self.chat_display.config(state=tk.NORMAL)

        if add_timestamp:
            timestamp = datetime.now().strftime("%H:%M:%S")
            time_prefix = f"[{timestamp}] "
        else:
            time_prefix = ""

        if role == "user":
            self.chat_display.insert(tk.END, f"{time_prefix}{self.user_display_name}: ", "user")
            self.chat_display.insert(tk.END, content + "\n\n")
        elif role == "assistant":
            self.chat_display.insert(tk.END, f"{time_prefix}{self.ai_display_name}: ", "assistant")
            self.chat_display.insert(tk.END, content + "\n\n")
        elif role == "reasoning":  # 思考过程消息
            self.chat_display.insert(tk.END, f"{time_prefix}思考过程: ", "reasoning")
            self.chat_display.insert(tk.END, content + "\n\n")
        else:  # system message
            self.chat_display.insert(tk.END, f"{time_prefix}系统: ", "system")
            self.chat_display.insert(tk.END, content + "\n\n")

        self.chat_display.config(state=tk.DISABLED)
        if add_timestamp:
            self.chat_display.see(tk.END)

    def send_message(self):
        """发送用户消息"""
        user_input = self.input_text.get("1.0", tk.END).strip()

        if not user_input:
            messagebox.showwarning("输入为空", "请输入您的问题")
            return

        # 清空输入框
        self.input_text.delete("1.0", tk.END)

        # 添加用户消息到显示区域
        self.add_message("user", user_input)

        # 添加到对话历史
        self.conversation_history.append({"role": "user", "content": user_input})

        # 更新状态
        self.status_var.set("AI正在思考中...")
        self.send_btn.config(state=tk.DISABLED)

        # 显示"正在思考"提示
        self.chat_display.config(state=tk.NORMAL)
        self.chat_display.insert(tk.END, f"{self.ai_display_name}: 正在思考...\n\n", "system")
        self.chat_display.config(state=tk.DISABLED)
        self.chat_display.see(tk.END)

        # 在新线程中调用API
        threading.Thread(target=self.get_ai_response, daemon=True).start()

    def get_ai_response(self):
        """从DeepSeek API获取AI响应"""
        try:
            # 检查API密钥
            if not self.api_key or self.api_key == "DeepSeek APIkey":
                self.root.after(0, self.show_error, "请先设置有效的API密钥")
                return

            # 构建请求参数
            request_params = {
                "model": self.current_model,
                "messages": self.conversation_history,
                "stream": False
            }

            # 如果是deepseek-reasoner模型，添加返回思考过程的参数
            if self.current_model == "deepseek-reasoner":
                request_params["extra_body"] = {"return_reasoning": True}

            # 调用DeepSeek API
            response = self.client.chat.completions.create(**request_params)

            # 获取响应内容
            if self.current_model == "deepseek-reasoner":
                # 对于推理模型，获取思考过程和最终答案
                reasoning_content = ""
                if hasattr(response.choices[0].message, 'model_extra') and response.choices[0].message.model_extra:
                    reasoning_content = response.choices[0].message.model_extra.get('reasoning_content', '')
                ai_response = response.choices[0].message.content
            else:
                reasoning_content = ""
                ai_response = response.choices[0].message.content

            # 在主线程中更新UI
            self.root.after(0, self.update_ai_response, ai_response, reasoning_content)

        except Exception as e:
            error_msg = f"API调用错误: {str(e)}"
            self.root.after(0, self.show_error, error_msg)

    def update_ai_response(self, response, reasoning_content=""):
        """更新AI响应到界面"""
        # 移除"正在思考"提示
        self.chat_display.config(state=tk.NORMAL)
        self.chat_display.delete("end-3l", "end-1l")

        # 如果有思考过程内容，先显示思考过程
        if reasoning_content and self.current_model == "deepseek-reasoner":
            self.add_message("reasoning", reasoning_content)

            # 添加分隔线
            self.chat_display.config(state=tk.NORMAL)
            self.chat_display.insert(tk.END, "─" * 50 + "\n\n", "system")
            self.chat_display.config(state=tk.DISABLED)

        # 添加实际响应
        self.add_message("assistant", response)

        # 添加到对话历史（只保存最终回答，不保存思考过程）
        self.conversation_history.append({"role": "assistant", "content": response})

        # 恢复按钮状态
        self.send_btn.config(state=tk.NORMAL)
        self.status_var.set(f"就绪 | 模型: {self.current_model} | 对话长度: {len(self.conversation_history)}")

    def show_error(self, error_msg):
        """显示错误信息"""
        # 移除"正在思考"提示
        self.chat_display.config(state=tk.NORMAL)
        self.chat_display.delete("end-3l", "end-1l")

        # 添加错误信息
        self.chat_display.insert(tk.END, "错误: ", "error")
        self.chat_display.insert(tk.END, error_msg + "\n\n")
        self.chat_display.config(state=tk.DISABLED)
        self.chat_display.see(tk.END)

        # 恢复按钮状态
        self.send_btn.config(state=tk.NORMAL)
        self.status_var.set("错误 - 请检查API设置")

        messagebox.showerror("API错误", error_msg)

    def clear_conversation(self):
        """清空对话历史"""
        if messagebox.askyesno("清空对话", "确定要清空当前对话历史吗？"):
            self.conversation_history = [
                {"role": "system", "content": self.system_prompt}
            ]

            self.chat_display.config(state=tk.NORMAL)
            self.chat_display.delete("1.0", tk.END)
            self.chat_display.config(state=tk.DISABLED)

            self.add_message("system", "对话已清空")
            self.status_var.set(f"对话已清空 | 模型: {self.current_model}")

    def save_conversation(self):
        """保存对话历史到文件"""
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"conversation_{timestamp}.json"

            with open(filename, 'w', encoding='utf-8') as f:
                json.dump(self.conversation_history, f, ensure_ascii=False, indent=2)

            messagebox.showinfo("保存成功", f"对话已保存到: {filename}")
            self.add_message("system", f"对话历史已保存到: {filename}")

        except Exception as e:
            messagebox.showerror("保存失败", f"保存文件时出错: {str(e)}")

    def load_conversation(self):
        """加载对话历史从文件"""
        filename = filedialog.askopenfilename(
            title="选择对话文件",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )

        if not filename:
            return

        try:
            with open(filename, 'r', encoding='utf-8') as f:
                loaded_history = json.load(f)

            # 验证加载的数据格式
            if not isinstance(loaded_history, list) or len(loaded_history) == 0:
                raise ValueError("无效的对话文件格式")

            # 更新对话历史
            self.conversation_history = loaded_history

            # 清空并重新填充显示区域
            self.chat_display.config(state=tk.NORMAL)
            self.chat_display.delete("1.0", tk.END)

            # 重新显示所有消息
            for msg in loaded_history:
                if msg["role"] == "system" and msg != loaded_history[0]:
                    continue
                self.add_message(msg["role"], msg["content"], False)

            self.chat_display.config(state=tk.DISABLED)
            self.status_var.set(f"对话已加载 | 模型: {self.current_model} | 记录数: {len(loaded_history)}")
            messagebox.showinfo("加载成功", f"已从 {os.path.basename(filename)} 加载对话记录")

        except Exception as e:
            messagebox.showerror("加载失败", f"加载文件时出错: {str(e)}")

    def show_settings_window(self):
        """显示集成设置窗口"""
        self.settings_window = tk.Toplevel(self.root)
        self.settings_window.title("应用设置")
        self.settings_window.geometry("600x500")
        self.settings_window.configure(bg="#f0f2f5")
        self.settings_window.grab_set()

        # 创建Notebook（选项卡控件）
        notebook = ttk.Notebook(self.settings_window)
        notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 创建三个选项卡框架
        api_frame = ttk.Frame(notebook, padding=15)
        prompt_frame = ttk.Frame(notebook, padding=15)
        display_frame = ttk.Frame(notebook, padding=15)

        # 添加选项卡
        notebook.add(api_frame, text="API设置")
        notebook.add(prompt_frame, text="提示词设置")
        notebook.add(display_frame, text="显示设置")

        # 填充每个选项卡的内容
        self.setup_api_tab(api_frame)
        self.setup_prompt_tab(prompt_frame)
        self.setup_display_tab(display_frame)

        # 底部按钮框架
        bottom_frame = ttk.Frame(self.settings_window)
        bottom_frame.pack(fill=tk.X, padx=10, pady=10)

        def apply_settings():
            """应用所有设置"""
            # 保存所有设置并检查是否都成功
            if (self.save_api_settings() and
                    self.save_prompt_settings() and
                    self.save_display_settings()):

                # 保存配置到文件
                self.save_config()

                messagebox.showinfo("保存成功", "所有设置已保存并生效")
                self.settings_window.destroy()
            else:
                # 如果有任何设置保存失败，显示错误信息
                messagebox.showwarning("保存失败", "部分设置未正确保存，请检查输入")

        apply_btn = ttk.Button(bottom_frame, text="应用所有设置", command=apply_settings)
        apply_btn.pack(side=tk.RIGHT, padx=5)

        cancel_btn = ttk.Button(bottom_frame, text="取消", command=self.settings_window.destroy)
        cancel_btn.pack(side=tk.RIGHT, padx=5)

    def setup_api_tab(self, parent):
        """设置API选项卡内容"""
        title_label = ttk.Label(parent, text="DeepSeek API设置",
                                font=("Arial", 12, "bold"))
        title_label.grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 15))

        ttk.Label(parent, text="API密钥:").grid(row=1, column=0, sticky="w", pady=5)

        # 使用StringVar来跟踪API密钥的变化
        self.api_key_var = tk.StringVar(value=self.api_key)
        self.api_entry = ttk.Entry(parent, width=50, show="*", textvariable=self.api_key_var)
        self.api_entry.grid(row=1, column=1, pady=5, padx=10, sticky="w")

        def test_connection():
            test_key = self.api_key_var.get().strip()
            if not test_key:
                messagebox.showwarning("输入错误", "请输入API密钥")
                return

            try:
                test_client = OpenAI(api_key=test_key, base_url="https://api.deepseek.com")
                test_response = test_client.chat.completions.create(
                    model="deepseek-chat",
                    messages=[{"role": "user", "content": "测试连接"}],
                    max_tokens=5
                )
                messagebox.showinfo("测试成功", "API连接测试成功！")
            except Exception as e:
                messagebox.showerror("测试失败", f"连接测试失败: {str(e)}")

        test_btn = ttk.Button(parent, text="测试连接", command=test_connection)
        test_btn.grid(row=2, column=1, pady=10, sticky="w")

        help_text = "请输入您的DeepSeek API密钥。您可以在DeepSeek官网获取API密钥。"
        help_label = ttk.Label(parent, text=help_text, wraplength=400, foreground="#666666")
        help_label.grid(row=3, column=0, columnspan=2, sticky="w", pady=10)

    def setup_prompt_tab(self, parent):
        """设置提示词选项卡内容"""
        title_label = ttk.Label(parent, text="系统提示词设置",
                                font=("Arial", 12, "bold"))
        title_label.grid(row=0, column=0, sticky="w", pady=(0, 10))

        help_text = "系统提示词定义了AI助手的基本行为和回复风格。修改提示词会影响AI的回复方式。"
        help_label = ttk.Label(parent, text=help_text, wraplength=400, foreground="#666666")
        help_label.grid(row=1, column=0, sticky="w", pady=(0, 10))

        self.prompt_text = scrolledtext.ScrolledText(parent, wrap=tk.WORD,
                                                     height=10, font=("Arial", 10),
                                                     padx=10, pady=10)
        # 设置当前系统提示词
        self.prompt_text.insert("1.0", self.system_prompt)
        self.prompt_text.grid(row=2, column=0, sticky="nsew", pady=5)

        parent.grid_rowconfigure(2, weight=1)
        parent.grid_columnconfigure(0, weight=1)

        prompt_btn_frame = ttk.Frame(parent)
        prompt_btn_frame.grid(row=3, column=0, sticky="w", pady=10)

        def reset_prompt():
            default_prompt = "你是一个乐于助人的AI助手，请用简洁友好的方式回答用户问题。"
            self.prompt_text.delete("1.0", tk.END)
            self.prompt_text.insert("1.0", default_prompt)

        reset_btn = ttk.Button(prompt_btn_frame, text="恢复默认", command=reset_prompt)
        reset_btn.pack(side=tk.LEFT, padx=5)

    def setup_display_tab(self, parent):
        """设置显示选项卡内容"""
        title_label = ttk.Label(parent, text="显示设置",
                                font=("Arial", 12, "bold"))
        title_label.grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 15))

        ttk.Label(parent, text="您的显示名称:").grid(row=1, column=0, sticky="w", pady=10)

        # 使用StringVar来跟踪显示名称的变化
        self.user_name_var = tk.StringVar(value=self.user_display_name)
        self.user_entry = ttk.Entry(parent, width=20, textvariable=self.user_name_var)
        self.user_entry.grid(row=1, column=1, pady=10, padx=10, sticky="w")

        ttk.Label(parent, text="AI助手名称:").grid(row=2, column=0, sticky="w", pady=10)

        # 使用StringVar来跟踪AI名称的变化
        self.ai_name_var = tk.StringVar(value=self.ai_display_name)
        self.ai_entry = ttk.Entry(parent, width=20, textvariable=self.ai_name_var)
        self.ai_entry.grid(row=2, column=1, pady=10, padx=10, sticky="w")

        preview_frame = ttk.LabelFrame(parent, text="预览", padding=10)
        preview_frame.grid(row=3, column=0, columnspan=2, pady=20, sticky="we")

        self.preview_text = tk.Text(preview_frame, height=4, width=50, font=("Arial", 9))
        self.preview_text.pack(fill=tk.BOTH)
        self.preview_text.config(state=tk.DISABLED)

        def update_preview():
            user_name = self.user_name_var.get() or "您"
            ai_name = self.ai_name_var.get() or "AI助手"

            self.preview_text.config(state=tk.NORMAL)
            self.preview_text.delete("1.0", tk.END)
            self.preview_text.insert("1.0",
                                     f"[12:00:00] {user_name}: 这是一个示例问题\n\n"
                                     f"[12:00:01] {ai_name}: 这是一个示例回答\n\n")
            self.preview_text.config(state=tk.DISABLED)

        # 初始更新预览
        update_preview()

        # 绑定变量变化事件
        self.user_name_var.trace("w", lambda *args: update_preview())
        self.ai_name_var.trace("w", lambda *args: update_preview())

    def save_api_settings(self):
        """保存API设置"""
        new_key = self.api_key_var.get().strip()
        if not new_key:
            messagebox.showwarning("输入错误", "API密钥不能为空")
            return False

        self.api_key = new_key
        self.client = OpenAI(api_key=new_key, base_url="https://api.deepseek.com")
        return True

    def save_prompt_settings(self):
        """保存提示词设置"""
        new_prompt = self.prompt_text.get("1.0", tk.END).strip()
        if not new_prompt:
            messagebox.showwarning("输入错误", "提示词不能为空")
            return False

        self.system_prompt = new_prompt

        # 更新conversation_history中的system prompt
        if self.conversation_history and self.conversation_history[0]["role"] == "system":
            self.conversation_history[0]["content"] = new_prompt
        else:
            self.conversation_history.insert(0, {"role": "system", "content": new_prompt})

        return True

    def save_display_settings(self):
        """保存显示设置"""
        user_name = self.user_name_var.get().strip()
        ai_name = self.ai_name_var.get().strip()

        # 验证输入
        if not user_name:
            messagebox.showwarning("输入错误", "用户名不能为空")
            return False
        if not ai_name:
            messagebox.showwarning("输入错误", "AI名称不能为空")
            return False

        self.user_display_name = user_name
        self.ai_display_name = ai_name

        # 更新状态栏
        self.status_var.set(f"就绪 | 模型: {self.current_model} | 用户: {user_name}")

        return True


def main():
    """主函数"""
    root = tk.Tk()
    app = DeepSeekChatApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()